package uk.co.hs.dao.generic;

import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.criterion.Disjunction;
import org.hibernate.criterion.Restrictions;

/**
 * Option.
 *
 * @param <T>
 */
public class Option<T>
{
  public static enum MatchType {
    EQUAL,
    EQUAL_OR_NULL,
    LESS_THAN,
    LESS_THAN_OR_NULL,
    LESS_THAN_OR_EQUAL,
    GREATER_THAN,
    GREATER_THAN_OR_NULL,
    GREATER_THAN_OR_EQUAL,
    GREATER_THAN_OR_EQUAL_OR_NULL,
    IN
  };

  MatchType mMatchType = MatchType.EQUAL;

  boolean mValueSet = false;
  T mValue;

  public T get()
  {
    return mValue;
  }

  public Option<T> set(T aValue)
  {
    mValueSet = true;
    mValue = aValue;
    return this;
  }

  public MatchType getMatchType()
  {
    return mMatchType;
  }

  public void setMatchType(MatchType aMatchType)
  {
    mMatchType = aMatchType;
  }


  public boolean isSet()
  {
    return mValueSet;
  }

  public Option<T> unset()
  {
    mValue = null;
    mValueSet = false;
    return this;
  }

  public Option(T aValue)
  {
    set(aValue);
  }

  public Option()
  {

  }

  /**
   * Apply option to criteria
   * @param aCrit
   * @param aPropertyName
   * @param aMatchType
   */
  @SuppressWarnings("rawtypes")
  public void apply(Criteria aCrit, String aPropertyName)
  {
    if(mMatchType.equals(MatchType.EQUAL))
    {
      if(mValue == null)
      {
        aCrit.add(Restrictions.isNull(aPropertyName));
      }
      else
      {
        aCrit.add(Restrictions.eq(aPropertyName, mValue));
      }
    }

    if(mMatchType.equals(MatchType.IN))
    {
      aCrit.add(Restrictions.in(aPropertyName, (List)mValue));
    }

    else if(mMatchType.equals(MatchType.EQUAL_OR_NULL))
    {
      aCrit.add(Restrictions.eqOrIsNull(aPropertyName, mValue));
    }

    else if(mMatchType.equals(MatchType.GREATER_THAN))
    {
      aCrit.add(Restrictions.gt(aPropertyName, mValue));
    }

    else if(mMatchType.equals(MatchType.GREATER_THAN_OR_EQUAL))
    {
      aCrit.add(Restrictions.ge(aPropertyName, mValue));
    }

    else if(mMatchType.equals(MatchType.GREATER_THAN_OR_NULL))
    {
      Disjunction or = Restrictions.disjunction();
      or.add(Restrictions.gt(aPropertyName, mValue));
      or.add(Restrictions.isNull(aPropertyName));
      aCrit.add(or);
    }

    else if(mMatchType.equals(MatchType.GREATER_THAN_OR_EQUAL_OR_NULL))
    {
      Disjunction or = Restrictions.disjunction();
      or.add(Restrictions.ge(aPropertyName, mValue));
      or.add(Restrictions.isNull(aPropertyName));
      aCrit.add(or);
    }

    else if(mMatchType.equals(MatchType.LESS_THAN))
    {
      aCrit.add(Restrictions.lt(aPropertyName, mValue));
    }

    else if(mMatchType.equals(MatchType.LESS_THAN_OR_EQUAL))
    {
      aCrit.add(Restrictions.le(aPropertyName, mValue));
    }

    else if(mMatchType.equals(MatchType.LESS_THAN_OR_NULL))
    {
      Disjunction or = Restrictions.disjunction();
      or.add(Restrictions.lt(aPropertyName, mValue));
      or.add(Restrictions.isNull(aPropertyName));
      aCrit.add(or);
    }
  }


};